package com.xxchat.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import javax.annotation.Resource;

import com.xxchat.entity.constants.QueryConstants;
import com.xxchat.entity.constants.SysConstants;
import com.xxchat.entity.dto.MessageSendDTO;
import com.xxchat.entity.dto.TokenUserInfoDTO;
import com.xxchat.entity.enums.*;
import com.xxchat.entity.po.*;
import com.xxchat.entity.query.*;
import com.xxchat.exception.BusinessException;
import com.xxchat.mappers.*;
import com.xxchat.redis.RedisComponent;
import com.xxchat.utils.CopyUtils;
import com.xxchat.websocket.MessageHandler;
import com.xxchat.websocket.utils.ChannelContextUtils;
import org.springframework.stereotype.Service;

import com.xxchat.entity.vo.PaginationResultVO;
import com.xxchat.service.UserContactApplyService;
import com.xxchat.utils.StringTools;
import org.springframework.transaction.annotation.Transactional;


/**
 * 联系人申请表 业务接口实现
 */
@Service("userContactApplyService")
public class UserContactApplyServiceImpl implements UserContactApplyService {

	@Resource
	private UserContactApplyMapper<UserContactApply, UserContactApplyQuery> userContactApplyMapper;

	@Resource
	private GroupInfoMapper<GroupInfo, GroupInfoQuery> groupInfoMapper;

	@Resource
	private UserContactMapper<UserContact, UserContactQuery> userContactMapper;

	@Resource
	private UserInfoMapper<UserInfo, UserInfoQuery> userInfoMapper;

	@Resource
	private ChatSessionMapper<ChatSession, ChatSessionQuery> chatSessionMapper;

	@Resource
	private ChatSessionUserMapper<ChatSessionUser, ChatSessionUserQuery> chatSessionUserMapper;

	@Resource
	private ChatMessageMapper<ChatMessage, ChatMessageQuery> chatMessageMapper;

	@Resource
	private RedisComponent redisComponent;

	@Resource
	private MessageHandler messageHandler;

	@Resource
	private ChannelContextUtils channelContextUtils;

	/**
	 * 根据条件查询列表
	 */
	@Override
	public List<UserContactApply> findListByParam(UserContactApplyQuery param) {
		return this.userContactApplyMapper.selectList(param);
	}

	/**
	 * 根据条件查询列表
	 */
	@Override
	public Integer findCountByParam(UserContactApplyQuery param) {
		return this.userContactApplyMapper.selectCount(param);
	}

	/**
	 * 分页查询方法
	 */
	@Override
	public PaginationResultVO<UserContactApply> findListByPage(UserContactApplyQuery param) {
		int count = this.findCountByParam(param);
		int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();

		SimplePage page = new SimplePage(param.getPageNo(), count, pageSize);
		param.setSimplePage(page);
		List<UserContactApply> list = this.findListByParam(param);
		PaginationResultVO<UserContactApply> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
		return result;
	}

	/**
	 * 新增
	 */
	@Override
	public Integer add(UserContactApply bean) {
		return this.userContactApplyMapper.insert(bean);
	}

	/**
	 * 批量新增
	 */
	@Override
	public Integer addBatch(List<UserContactApply> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.userContactApplyMapper.insertBatch(listBean);
	}

	/**
	 * 批量新增或者修改
	 */
	@Override
	public Integer addOrUpdateBatch(List<UserContactApply> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.userContactApplyMapper.insertOrUpdateBatch(listBean);
	}

	/**
	 * 多条件更新
	 */
	@Override
	public Integer updateByParam(UserContactApply bean, UserContactApplyQuery param) {
		StringTools.checkParam(param);
		return this.userContactApplyMapper.updateByParam(bean, param);
	}

	/**
	 * 多条件删除
	 */
	@Override
	public Integer deleteByParam(UserContactApplyQuery param) {
		StringTools.checkParam(param);
		return this.userContactApplyMapper.deleteByParam(param);
	}

	/**
	 * 根据ApplyId获取对象
	 */
	@Override
	public UserContactApply getUserContactApplyByApplyId(Integer applyId) {
		return this.userContactApplyMapper.selectByApplyId(applyId);
	}

	/**
	 * 根据ApplyId修改
	 */
	@Override
	public Integer updateUserContactApplyByApplyId(UserContactApply bean, Integer applyId) {
		return this.userContactApplyMapper.updateByApplyId(bean, applyId);
	}

	/**
	 * 根据ApplyId删除
	 */
	@Override
	public Integer deleteUserContactApplyByApplyId(Integer applyId) {
		return this.userContactApplyMapper.deleteByApplyId(applyId);
	}

	/**
	 * 根据ApplyUserIdAndReceiveUserIdAndContactId获取对象
	 */
	@Override
	public UserContactApply getUserContactApplyByApplyUserIdAndReceiveUserIdAndContactId(String applyUserId, String receiveUserId, String contactId) {
		return this.userContactApplyMapper.selectByApplyUserIdAndReceiveUserIdAndContactId(applyUserId, receiveUserId, contactId);
	}

	/**
	 * 根据ApplyUserIdAndReceiveUserIdAndContactId修改
	 */
	@Override
	public Integer updateUserContactApplyByApplyUserIdAndReceiveUserIdAndContactId(UserContactApply bean, String applyUserId, String receiveUserId, String contactId) {
		return this.userContactApplyMapper.updateByApplyUserIdAndReceiveUserIdAndContactId(bean, applyUserId, receiveUserId, contactId);
	}

	/**
	 * 根据ApplyUserIdAndReceiveUserIdAndContactId删除
	 */
	@Override
	public Integer deleteUserContactApplyByApplyUserIdAndReceiveUserIdAndContactId(String applyUserId, String receiveUserId, String contactId) {
		return this.userContactApplyMapper.deleteByApplyUserIdAndReceiveUserIdAndContactId(applyUserId, receiveUserId, contactId);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Integer apply(TokenUserInfoDTO userInfoDTO, String contactId, String applyInfo) {
		// 自己不能申请自己
		if (userInfoDTO.getUserId().equals(contactId)) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		// 判断是申请好友还是申请加入群
		UserContactTypeEnum contactType = UserContactTypeEnum.getByPrefix(contactId);
		if (contactType == null) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		// 判断申请info
		if (StringTools.isEmpty(applyInfo)) {
			applyInfo = String.format(SysConstants.APPLY_INFO_TEMPLATE, userInfoDTO.getNickName());
		}

		String receiveUserId = null;
		Integer joinType = null;
		UserContact userContact = this.userContactMapper.selectByContactIdAndUserId(contactId, userInfoDTO.getUserId());
		if (contactType == UserContactTypeEnum.USER) {
			// 如果是好友，看看是不是拉黑或者已经是好友了
			UserInfo userInfo = this.handleUserApply(userContact, contactId);
			receiveUserId = userInfo.getUserId();
			joinType = userInfo.getJoinType();
		} else {
			// 如果是群，就要发给群主，而且要看自己是不是已经在群里了
			GroupInfo groupInfo = this.groupInfoMapper.selectByGroupId(contactId);
			if (groupInfo == null || GroupStatusEnum.DISBAND.getStatus().equals(groupInfo.getStatus()) || groupInfo.getGroupOwnerId().equals(userInfoDTO.getUserId())) {
				throw new BusinessException(ResponseCodeEnum.CODE_600);
			}
			if (null != userContact && UserContactStatusEnum.BLACKLIST_BE.getStatus().equals(userContact.getStatus())) {
				throw new BusinessException(ResponseCodeEnum.CODE_701);
			}
			if (null != userContact && UserContactStatusEnum.FRIEND.getStatus().equals(userContact.getStatus())) {
				throw new BusinessException("你已经在群组中");
			}
			receiveUserId = groupInfo.getGroupOwnerId();
			joinType = groupInfo.getJoinType();
		}

		return this.handleApplyProcess(joinType, receiveUserId, contactId, userInfoDTO.getUserId(), applyInfo, contactType.getType());
	}

	private UserInfo handleUserApply(UserContact userContact, String contactId) {
		if (userContact != null && UserContactStatusEnum.BLACKLIST_BE.getStatus().equals(userContact.getStatus())) {
			throw new BusinessException(ResponseCodeEnum.CODE_701);
		}
		if (userContact != null &&
				(UserContactStatusEnum.FRIEND.getStatus().equals(userContact.getStatus()) ||
						UserContactStatusEnum.MYSELF.getStatus().equals(userContact.getStatus()))) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		UserInfo userInfo = this.userInfoMapper.selectByUserId(contactId);
		if (userInfo == null || UserStatusEnum.DISABLED.getStatus().equals(userInfo.getStatus())) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		return userInfo;
	}

	private Integer handleApplyProcess(Integer joinType,
									   String receiveUserId,
									   String contactId,
									   String userId,
									   String applyInfo,
									   Integer contactType) {
		// 直接加入不需要申请，就不用往数据库加apply
		if (JoinTypeEnum.JOIN.getType().equals(joinType)) {
			// 新增contact记录,添加联系人
			this.addContact(userId, receiveUserId, contactId, contactType, applyInfo);
			return joinType;
		}
		// 需要申请
		UserContactApply userContactApply = this.userContactApplyMapper.selectByApplyUserIdAndReceiveUserIdAndContactId(userId, receiveUserId, contactId);
		if (userContactApply == null) {
			// 首次申请
			userContactApply = new UserContactApply();
			userContactApply.setReceiveUserId(receiveUserId);
			userContactApply.setContactId(contactId);
			userContactApply.setApplyUserId(userId);
			userContactApply.setApplyInfo(applyInfo);
			userContactApply.setContactType(contactType);
			userContactApply.setStatus(UserContactApplyStatusEnum.PENDING.getStatus());
			userContactApply.setLastApplyTime(System.currentTimeMillis());
			userContactApplyMapper.insert(userContactApply);
		} else {
			// 再次申请
			if (UserContactApplyStatusEnum.BLACK.getStatus().equals(userContactApply.getStatus())) {
				throw new BusinessException(ResponseCodeEnum.CODE_701);
			}
			// TODO 计算申请时间间隔，不能连续多次申请
			userContactApply.setLastApplyTime(System.currentTimeMillis());
			userContactApply.setApplyInfo(applyInfo);
			this.userContactApplyMapper.updateByApplyId(userContactApply, userContactApply.getApplyId());
		}
		// 发送ws申请消息给用户
		MessageSendDTO<String> messageSendDTO = new MessageSendDTO<>();
		messageSendDTO.setMessageType(MessageTypeEnum.CONTACT_APPLY.getType());
		messageSendDTO.setMessageContent(applyInfo);
		messageSendDTO.setContactId(receiveUserId);
		messageHandler.sendMessage(messageSendDTO);

		return joinType;
	}

	@Override
	public PaginationResultVO<UserContactApply> loadApplyList(TokenUserInfoDTO userInfoDTO, Integer pageNo) {
		Integer count = this.userContactApplyMapper.selectApplyListCount(userInfoDTO.getUserId());

		// 2. 设置分页参数
		SimplePage page = new SimplePage(pageNo, count, PageSize.SIZE15.getSize());
		List<UserContactApply> applyList = this.userContactApplyMapper.selectApplyList(page, userInfoDTO.getUserId());
        return (PaginationResultVO<UserContactApply>) new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), applyList);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void handlerApply(String userId, Integer applyId, Integer status) {
		UserContactApplyStatusEnum statusEnum = UserContactApplyStatusEnum.getByStatus(status);
		if (statusEnum == null) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		UserContactApply applyInfo = this.userContactApplyMapper.selectByApplyId(applyId);
		if (applyInfo == null || !userId.equals(applyInfo.getReceiveUserId())) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}

		applyInfo.setStatus(statusEnum.getStatus());

		UserContactApplyQuery query = new UserContactApplyQuery();
		query.setApplyId(applyId);
		// 乐观锁
		query.setStatus(UserContactApplyStatusEnum.PENDING.getStatus());
		Integer res = this.userContactApplyMapper.updateByParam(applyInfo, query);
		if (res != 1) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}

		// 处理具体操作
		if (UserContactApplyStatusEnum.AGREE.getStatus().equals(statusEnum.getStatus())) {
			// 通过，添加联系人
			this.addContact(applyInfo.getApplyUserId(),
					applyInfo.getReceiveUserId(),
					applyInfo.getContactId(),
					applyInfo.getContactType(),
					applyInfo.getApplyInfo());
		} else if (UserContactApplyStatusEnum.BLACK.getStatus().equals(statusEnum.getStatus())) {
			// 拉黑
			Date curDate = new Date();
			UserContact userContact = new UserContact();
			userContact.setUserId(applyInfo.getApplyUserId());
			userContact.setContactId(applyInfo.getContactId());
			userContact.setContactType(applyInfo.getContactType());
			userContact.setCreateTime(curDate);
			userContact.setStatus(UserContactStatusEnum.BLACKLIST_BE.getStatus());
			userContact.setLastUpdateTime(curDate);
			userContactMapper.insertOrUpdate(userContact);
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void addContact(String applyUserId, String receiveUserId, String contactId, Integer contactType, String applyInfo) {
		if (UserContactTypeEnum.GROUP.getType().equals(contactType)) {
			// 如果是群聊
			UserContactQuery query = new UserContactQuery();
			query.setContactId(contactId);
			query.setStatus(UserContactStatusEnum.FRIEND.getStatus());
			Integer count = this.userContactMapper.selectCount(query);
			if (count >= redisComponent.getSysSetting().getMaxGroupMemberCount()) {
				throw new BusinessException(ResponseCodeEnum.CODE_801);
			}
		}
		// 同意，双方添加好友
		ArrayList<UserContact> contactList = new ArrayList<>();
		// 申请人添加对方
		UserContact userContact = new UserContact();
		userContact.setUserId(applyUserId);
		userContact.setContactId(contactId);
		userContact.setContactType(contactType);
		userContact.setCreateTime(new Date());
		userContact.setLastUpdateTime(new Date());
		userContact.setStatus(UserContactStatusEnum.FRIEND.getStatus());
		contactList.add(userContact);
		// 如果是添加好友，则加多一条接收人添加申请人，群组不用
		if (UserContactTypeEnum.USER.getType().equals(contactType)) {
			userContact = new UserContact();
			userContact.setUserId(receiveUserId);
			userContact.setContactId(applyUserId);
			userContact.setContactType(contactType);
			userContact.setCreateTime(new Date());
			userContact.setLastUpdateTime(new Date());
			userContact.setStatus(UserContactStatusEnum.FRIEND.getStatus());
			contactList.add(userContact);
		}
		// 批量插入
		this.userContactMapper.insertBatch(contactList);

		// 如果是好友，并且都已经同意了，添加缓存，发消息
		if (UserContactTypeEnum.USER.getType().equals(contactType)) {
			redisComponent.saveUserContact(receiveUserId, applyUserId);
		}
		redisComponent.saveUserContact(applyUserId, contactId);

		// 创建会话 发送消息
		String sessionId = null;
		if (UserContactTypeEnum.USER.getType().equals(contactType)) {
			sessionId = StringTools.getChatSessionId4User(new String[] {applyUserId, contactId});
		} else {
			sessionId = StringTools.getChatSessionId4Group(contactId);
		}

		ArrayList<ChatSessionUser> chatSessionUsers = new ArrayList<>();
		if (UserContactTypeEnum.USER.getType().equals(contactType)) {
			// 加好友
			ChatSession chatSession = new ChatSession();
			chatSession.setSessionId(sessionId);
			chatSession.setLastMessage(applyInfo);
			chatSession.setLastReceiveTime(System.currentTimeMillis());
			this.chatSessionMapper.insertOrUpdate(chatSession);

			// 申请人
			ChatSessionUser applyChatSessionUser = new ChatSessionUser();
			applyChatSessionUser.setUserId(applyUserId);
			applyChatSessionUser.setContactId(contactId);
			applyChatSessionUser.setSessionId(sessionId);
			UserInfo applyUserInfo = this.userInfoMapper.selectByUserId(contactId);
			applyChatSessionUser.setContactName(Objects.requireNonNull(applyUserInfo.getNickName()));
			chatSessionUsers.add(applyChatSessionUser);

			// 接收人
			ChatSessionUser contactSessionUser = new ChatSessionUser();
			contactSessionUser.setUserId(receiveUserId);
			contactSessionUser.setContactId(applyUserId);
			contactSessionUser.setSessionId(sessionId);
			UserInfo contactUserInfo = this.userInfoMapper.selectByUserId(applyUserId);
			contactSessionUser.setContactName(Objects.requireNonNull(contactUserInfo.getNickName()));
			chatSessionUsers.add(contactSessionUser);

			this.chatSessionUserMapper.insertOrUpdateBatch(chatSessionUsers);

			// 记录消息表
			ChatMessage message = new ChatMessage();
			message.setSessionId(sessionId);
			message.setMessageType(MessageTypeEnum.ADD_FRIEND.getType());
			message.setMessageContent(applyInfo);
			message.setSendUserId(applyUserId);
			message.setSendUserNickName(applyUserInfo.getNickName());
			message.setSendTime(System.currentTimeMillis());
			message.setContactId(contactId);
			message.setContactType(UserContactTypeEnum.USER.getType());
			this.chatMessageMapper.insert(message);

			MessageSendDTO messageSendDTO = CopyUtils.copy(message, MessageSendDTO.class);
			// 发送给接收好友申请的人
			messageHandler.sendMessage(messageSendDTO);
			// 发送给申请人
			messageSendDTO.setMessageType(MessageTypeEnum.ADD_FRIEND_SELF.getType());
			messageSendDTO.setContactId(applyUserId);
			messageSendDTO.setExtendData(contactUserInfo);
			messageHandler.sendMessage(messageSendDTO);
		} else {
			// 加群组
			ChatSessionUser chatSessionUser = new ChatSessionUser();
			chatSessionUser.setUserId(applyUserId);
			chatSessionUser.setContactId(contactId);
			GroupInfo groupInfo = this.groupInfoMapper.selectByGroupId(contactId);
			if (null == groupInfo) {
				throw new BusinessException(ResponseCodeEnum.CODE_600);
			}
			chatSessionUser.setContactName(groupInfo.getGroupName());
			chatSessionUser.setSessionId(sessionId);
			this.chatSessionUserMapper.insertOrUpdate(chatSessionUser);

			UserInfo applyUserInfo = this.userInfoMapper.selectByUserId(applyUserId);
			String sendMessage = String.format(MessageTypeEnum.ADD_GROUP.getInitMessage(), applyUserInfo.getNickName());

			// 增加session信息
			ChatSession chatSession = new ChatSession();
			chatSession.setSessionId(sessionId);
			chatSession.setLastMessage(sendMessage);
			chatSession.setLastReceiveTime(System.currentTimeMillis());
			this.chatSessionMapper.insertOrUpdate(chatSession);

			// 创建聊天消息
			ChatMessage chatMessage = new ChatMessage();
			chatMessage.setSessionId(sessionId);
			chatMessage.setMessageType(MessageTypeEnum.ADD_GROUP.getType());
			chatMessage.setMessageContent(sendMessage);
			chatMessage.setSendTime(System.currentTimeMillis());
			chatMessage.setContactId(contactId);
			chatMessage.setContactType(UserContactTypeEnum.GROUP.getType());
			chatMessage.setStatus(MessageStatusEnum.SENDED.getStatus());
			this.chatMessageMapper.insert(chatMessage);

			// 将群组添加到联系人
			this.redisComponent.saveUserContact(applyUserId, groupInfo.getGroupId());

			// 将联系人通道添加到群组通道
			this.channelContextUtils.addUser2Group(applyUserId, groupInfo.getGroupId());

			// 发送群消息
			MessageSendDTO messageSendDTO = CopyUtils.copy(chatMessage, MessageSendDTO.class);
			messageSendDTO.setExtendData(chatSessionUser);
			messageSendDTO.setContactName(groupInfo.getGroupName());
			messageSendDTO.setLastMessage(chatSessionUser.getLastMessage());

			// 获取群员数量
			UserContactQuery query = new UserContactQuery();
			query.setContactId(contactId);
			query.setStatus(UserContactStatusEnum.FRIEND.getStatus());
			Integer count = this.userContactMapper.selectCount(query);
			messageSendDTO.setMemberCount(count);

			messageHandler.sendMessage(messageSendDTO);
		}
	}
}